<!--
 - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
 - SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
	<NcDialog
		id="unified-search"
		ref="unifiedSearchModal"
		class="unified-search-modal-root"
		content-classes="unified-search-modal__content"
		dialog-classes="unified-search-modal"
		:name="t('core', 'Unified search')"
		:open="open"
		size="normal"
		@update:open="onUpdateOpen">
		<!-- Modal for picking custom time range -->
		<CustomDateRangeModal
			:is-open="showDateRangeModal"
			class="unified-search__date-range"
			@set:custom-date-range="setCustomDateRange"
			@update:is-open="showDateRangeModal = $event" />

		<!-- Unified search form -->
		<div class="unified-search-modal__header">
			<NcInputField
				ref="searchInput"
				v-model="searchQuery"
				data-cy-unified-search-input
				type="text"
				:label="t('core', 'Search apps, files, tags, messages') + '...'"
				@update:value="debouncedFind" />
			<div class="unified-search-modal__filters" data-cy-unified-search-filters>
				<NcActions :menu-name="t('core', 'Places')" :open.sync="providerActionMenuIsOpen" data-cy-unified-search-filter="places">
					<template #icon>
						<IconListBox :size="20" />
					</template>
					<!-- Provider id's may be duplicated since, plugin filters could depend on a provider that is already in the defaults.
					provider.id concatenated to provider.name is used to create the item id, if same then, there should be an issue. -->
					<NcActionButton
						v-for="provider in providers"
						:key="`${provider.id}-${provider.name.replace(/\s/g, '')}`"
						:disabled="provider.disabled"
						@click="addProviderFilter(provider)">
						<template #icon>
							<img :src="provider.icon" class="filter-button__icon" alt="">
						</template>
						{{ provider.name }}
					</NcActionButton>
				</NcActions>
				<NcActions :menu-name="t('core', 'Date')" :open.sync="dateActionMenuIsOpen" data-cy-unified-search-filter="date">
					<template #icon>
						<IconCalendarRange :size="20" />
					</template>
					<NcActionButton :close-after-click="true" @click="applyQuickDateRange('today')">
						{{ t('core', 'Today') }}
					</NcActionButton>
					<NcActionButton :close-after-click="true" @click="applyQuickDateRange('7days')">
						{{ t('core', 'Last 7 days') }}
					</NcActionButton>
					<NcActionButton :close-after-click="true" @click="applyQuickDateRange('30days')">
						{{ t('core', 'Last 30 days') }}
					</NcActionButton>
					<NcActionButton :close-after-click="true" @click="applyQuickDateRange('thisyear')">
						{{ t('core', 'This year') }}
					</NcActionButton>
					<NcActionButton :close-after-click="true" @click="applyQuickDateRange('lastyear')">
						{{ t('core', 'Last year') }}
					</NcActionButton>
					<NcActionButton :close-after-click="true" @click="applyQuickDateRange('custom')">
						{{ t('core', 'Custom date range') }}
					</NcActionButton>
				</NcActions>
				<SearchableList
					:label-text="t('core', 'Search people')"
					:search-list="userContacts"
					:empty-content-text="t('core', 'Not found')"
					data-cy-unified-search-filter="people"
					@search-term-change="debouncedFilterContacts"
					@item-selected="applyPersonFilter">
					<template #trigger>
						<NcButton>
							<template #icon>
								<IconAccountGroup :size="20" />
							</template>
							{{ t('core', 'People') }}
						</NcButton>
					</template>
				</SearchableList>
				<NcButton v-if="localSearch" data-cy-unified-search-filter="current-view" @click="searchLocally">
					{{ t('core', 'Filter in current view') }}
					<template #icon>
						<IconFilter :size="20" />
					</template>
				</NcButton>
				<NcCheckboxRadioSwitch
					v-if="hasExternalResources"
					v-model="searchExternalResources"
					type="switch"
					class="unified-search-modal__search-external-resources"
					:class="{ 'unified-search-modal__search-external-resources--aligned': localSearch }">
					{{ t('core', 'Search connected services') }}
				</NcCheckboxRadioSwitch>
			</div>
			<div class="unified-search-modal__filters-applied">
				<FilterChip
					v-for="filter in filters"
					:key="filter.id"
					:text="filter.name ?? filter.text"
					pretext=""
					@delete="removeFilter(filter)">
					<template #icon>
						<NcAvatar
							v-if="filter.type === 'person'"
							:user="filter.user"
							:size="24"
							disable-menu
							hide-user-status
							:hide-favorite="false" />
						<IconCalendarRange v-else-if="filter.type === 'date'" />
						<img v-else :src="filter.icon" alt="">
					</template>
				</FilterChip>
			</div>
		</div>

		<div v-if="showEmptyContentInfo" class="unified-search-modal__no-content">
			<NcEmptyContent :name="emptyContentMessage">
				<template #icon>
					<IconMagnify :size="64" />
				</template>
			</NcEmptyContent>
		</div>

		<div v-else class="unified-search-modal__results">
			<h3 class="hidden-visually">
				{{ t('core', 'Results') }}
			</h3>
			<div v-for="providerResult in results" :key="providerResult.id" class="result">
				<h4 :id="`unified-search-result-${providerResult.id}`" class="result-title">
					{{ providerResult.name }}
				</h4>
				<ul class="result-items" :aria-labelledby="`unified-search-result-${providerResult.id}`">
					<SearchResult
						v-for="(result, index) in providerResult.results"
						:key="index"
						v-bind="result" />
				</ul>
				<div class="result-footer">
					<NcButton v-if="providerResult.results.length === providerResult.limit" variant="tertiary-no-background" @click="loadMoreResultsForProvider(providerResult)">
						{{ t('core', 'Load more results') }}
						<template #icon>
							<IconDotsHorizontal :size="20" />
						</template>
					</NcButton>
					<NcButton v-if="providerResult.inAppSearch" alignment="end-reverse" variant="tertiary-no-background">
						{{ t('core', 'Search in') }} {{ providerResult.name }}
						<template #icon>
							<IconArrowRight :size="20" />
						</template>
					</NcButton>
				</div>
			</div>
		</div>
	</NcDialog>
</template>

<script lang="ts">
import { subscribe } from '@nextcloud/event-bus'
import { loadState } from '@nextcloud/initial-state'
import { getCanonicalLocale, t } from '@nextcloud/l10n'
import { useBrowserLocation } from '@vueuse/core'
import debounce from 'debounce'
import { defineComponent } from 'vue'
import NcActionButton from '@nextcloud/vue/components/NcActionButton'
import NcActions from '@nextcloud/vue/components/NcActions'
import NcAvatar from '@nextcloud/vue/components/NcAvatar'
import NcButton from '@nextcloud/vue/components/NcButton'
import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'
import NcDialog from '@nextcloud/vue/components/NcDialog'
import NcEmptyContent from '@nextcloud/vue/components/NcEmptyContent'
import NcInputField from '@nextcloud/vue/components/NcInputField'
import IconAccountGroup from 'vue-material-design-icons/AccountGroupOutline.vue'
import IconArrowRight from 'vue-material-design-icons/ArrowRight.vue'
import IconCalendarRange from 'vue-material-design-icons/CalendarRangeOutline.vue'
import IconDotsHorizontal from 'vue-material-design-icons/DotsHorizontal.vue'
import IconFilter from 'vue-material-design-icons/Filter.vue'
import IconListBox from 'vue-material-design-icons/ListBox.vue'
import IconMagnify from 'vue-material-design-icons/Magnify.vue'
import CustomDateRangeModal from './CustomDateRangeModal.vue'
import SearchableList from './SearchableList.vue'
import FilterChip from './SearchFilterChip.vue'
import SearchResult from './SearchResult.vue'
import { unifiedSearchLogger } from '../../logger.js'
import { getContacts, getProviders, search as unifiedSearch } from '../../services/UnifiedSearchService.js'
import { useSearchStore } from '../../store/unified-search-external-filters.js'

export default defineComponent({
	name: 'UnifiedSearchModal',
	components: {
		IconArrowRight,
		IconAccountGroup,
		IconCalendarRange,
		IconDotsHorizontal,
		IconFilter,
		IconListBox,
		IconMagnify,

		CustomDateRangeModal,
		FilterChip,
		NcActions,
		NcActionButton,
		NcAvatar,
		NcButton,
		NcEmptyContent,
		NcDialog,
		NcInputField,
		NcCheckboxRadioSwitch,
		SearchableList,
		SearchResult,
	},

	props: {
		/**
		 * Open state of the modal
		 */
		open: {
			type: Boolean,
			required: true,
		},

		/**
		 * The current query string
		 */
		query: {
			type: String,
			default: '',
		},

		/**
		 * If the current page / app supports local search
		 */
		localSearch: {
			type: Boolean,
			default: false,
		},
	},

	emits: ['update:open', 'update:query'],

	setup() {
		/**
		 * Reactive version of window.location
		 */
		const currentLocation = useBrowserLocation()
		const searchStore = useSearchStore()
		return {
			t,

			currentLocation,
			externalFilters: searchStore.externalFilters,
		}
	},

	data() {
		return {
			providers: [],
			providerActionMenuIsOpen: false,
			dateActionMenuIsOpen: false,
			providerResultLimit: 5,
			dateFilter: {
				id: 'date',
				type: 'date',
				text: '',
				startFrom: null as Date | null,
				endAt: null as Date | null,
			},

			personFilter: { id: 'person', type: 'person', name: '' },
			filteredProviders: [],
			searching: false,
			searchQuery: '',
			lastSearchQuery: '',
			placessearchTerm: '',
			dateTimeFilter: null,
			filters: [],
			results: [],
			contacts: [],
			showDateRangeModal: false,
			internalIsVisible: this.open,
			initialized: false,
			searchExternalResources: false,
			minSearchLength: loadState('unified-search', 'min-search-length', 1),
		}
	},

	computed: {
		isEmptySearch() {
			return this.searchQuery.length === 0
		},

		hasNoResults() {
			return !this.isEmptySearch && this.results.length === 0
		},

		isSearchQueryTooShort() {
			return this.searchQuery.length < this.minSearchLength
		},

		showEmptyContentInfo() {
			return this.isEmptySearch || this.hasNoResults
		},

		emptyContentMessage() {
			if (this.searching && this.hasNoResults) {
				return t('core', 'Searching …')
			}

			if (this.isSearchQueryTooShort) {
				switch (this.minSearchLength) {
					case 1:
						return t('core', 'Start typing to search')
					default:
						return t('core', 'Minimum search length is {minSearchLength} characters', { minSearchLength: this.minSearchLength })
				}
			}

			return t('core', 'No matching results')
		},

		userContacts() {
			return this.contacts
		},

		debouncedFind() {
			return debounce(this.find, 300)
		},

		debouncedFilterContacts() {
			return debounce(this.filterContacts, 300)
		},

		hasExternalResources() {
			return this.providers.some((provider) => provider.isExternalProvider)
		},
	},

	watch: {
		open() {
			// Load results when opened with already filled query
			if (this.open) {
				this.focusInput()
				if (!this.initialized) {
					Promise.all([getProviders(), getContacts({ searchTerm: '' })])
						.then(([providers, contacts]) => {
							this.providers = this.groupProvidersByApp([...providers, ...this.externalFilters])
							this.contacts = this.mapContacts(contacts)
							unifiedSearchLogger.debug('Search providers and contacts initialized:', { providers: this.providers, contacts: this.contacts })
							this.initialized = true
						})
						.catch((error) => {
							unifiedSearchLogger.error(error)
						})
				}
				if (this.searchQuery) {
					this.find(this.searchQuery)
				}
			}
		},

		query: {
			immediate: true,
			handler() {
				this.searchQuery = this.query
			},
		},

		searchQuery: {
			handler() {
				this.$emit('update:query', this.searchQuery)
			},
		},

		searchExternalResources() {
			if (this.searchQuery) {
				this.find(this.searchQuery)
			}
		},
	},

	mounted() {
		subscribe('nextcloud:unified-search:add-filter', this.handlePluginFilter)
	},

	methods: {
		/**
		 * On close the modal is closed and the query is reset
		 *
		 * @param open The new open state
		 */
		onUpdateOpen(open: boolean) {
			if (!open) {
				this.$emit('update:open', false)
				this.$emit('update:query', '')
			}
		},

		/**
		 * Only close the modal but keep the query for in-app search
		 */
		searchLocally() {
			this.$emit('update:query', this.searchQuery)
			this.$emit('update:open', false)
		},

		focusInput() {
			this.$nextTick(() => {
				this.$refs.searchInput?.focus()
			})
		},

		find(query: string, providersToSearchOverride = null) {
			if (this.isSearchQueryTooShort) {
				this.results = []
				this.searching = false
				return
			}

			// Reset the provider result limit when performing a new search
			if (query !== this.lastSearchQuery) {
				this.providerResultLimit = 5
			}
			this.lastSearchQuery = query

			this.searching = true
			const newResults = []
			const providersToSearch = providersToSearchOverride || (this.filteredProviders.length > 0 ? this.filteredProviders : this.providers)
			const searchProvider = (provider) => {
				const params = {
					type: provider.searchFrom ?? provider.id,
					query,
					cursor: null,
					extraQueries: provider.extraParams,
				}

				// This block of filter checks should be dynamic somehow and should be handled in
				// nextcloud/search lib
				const activeFilters = this.filters.filter((filter) => {
					return filter.type !== 'provider' && this.providerIsCompatibleWithFilters(provider, [filter.type])
				})

				activeFilters.forEach((filter) => {
					switch (filter.type) {
						case 'date':
							if (provider.filters?.since && provider.filters?.until) {
								params.since = this.dateFilter.startFrom
								params.until = this.dateFilter.endAt
							}
							break
						case 'person':
							if (provider.filters?.person) {
								params.person = this.personFilter.user
							}
							break
					}
				})

				if (this.providerResultLimit > 5) {
					params.limit = this.providerResultLimit
					unifiedSearchLogger.debug('Limiting search to', params.limit)
				}

				const shouldSkipSearch = !this.searchExternalResources && provider.isExternalProvider
				const wasManuallySelected = this.filteredProviders.some((filteredProvider) => filteredProvider.id === provider.id)
				// if the provider is an external resource and the user has not manually selected it, skip the search
				if (shouldSkipSearch && !wasManuallySelected) {
					this.searching = false
					return
				}

				const request = unifiedSearch(params).request

				request().then((response) => {
					newResults.push({
						...provider,
						results: response.data.ocs.data.entries,
						limit: params.limit ?? 5,
					})

					unifiedSearchLogger.debug('Unified search results:', { results: this.results, newResults })

					this.updateResults(newResults)
					this.searching = false
				})
			}

			providersToSearch.forEach(searchProvider)
		},

		updateResults(newResults) {
			let updatedResults = [...this.results]
			// If filters are applied, remove any previous results for providers that are not in current filters
			if (this.filters.length > 0) {
				updatedResults = updatedResults.filter((result) => {
					return this.filters.some((filter) => filter.id === result.id)
				})
			}
			// Process the new results
			newResults.forEach((newResult) => {
				const existingResultIndex = updatedResults.findIndex((result) => result.id === newResult.id)
				if (existingResultIndex !== -1) {
					if (newResult.results.length === 0) {
						// If the new results data has no matches for and existing result, remove the existing result
						updatedResults.splice(existingResultIndex, 1)
					} else {
						// If input triggered a change in existing results, update existing result
						updatedResults.splice(existingResultIndex, 1, newResult)
					}
				} else if (newResult.results.length > 0) {
					// Push the new result to the array only if its results array is not empty
					updatedResults.push(newResult)
				}
			})
			const sortedResults = updatedResults.slice(0)
			// Order results according to provider preference
			sortedResults.sort((a, b) => {
				const aProvider = this.providers.find((provider) => provider.id === a.id)
				const bProvider = this.providers.find((provider) => provider.id === b.id)
				const aOrder = aProvider ? aProvider.order : 0
				const bOrder = bProvider ? bProvider.order : 0
				return aOrder - bOrder
			})
			this.results = sortedResults
		},

		mapContacts(contacts) {
			return contacts.map((contact) => {
				return {
					// id: contact.id,
					// name: '',
					displayName: contact.fullName,
					isNoUser: false,
					subname: contact.emailAddresses[0] ? contact.emailAddresses[0] : '',
					icon: '',
					user: contact.id,
					isUser: contact.isUser,
				}
			})
		},

		filterContacts(query) {
			getContacts({ searchTerm: query }).then((contacts) => {
				this.contacts = this.mapContacts(contacts)
				unifiedSearchLogger.debug(`Contacts filtered by ${query}`, { contacts: this.contacts })
			})
		},

		applyPersonFilter(person) {
			const existingPersonFilter = this.filters.findIndex((filter) => filter.id === person.id)
			if (existingPersonFilter === -1) {
				this.personFilter.id = person.id
				this.personFilter.user = person.user
				this.personFilter.name = person.displayName
				this.filters.push(this.personFilter)
			} else {
				this.filters[existingPersonFilter].id = person.id
				this.filters[existingPersonFilter].user = person.user
				this.filters[existingPersonFilter].name = person.displayName
			}

			this.providers.forEach(async (provider, index) => {
				this.providers[index].disabled = !(await this.providerIsCompatibleWithFilters(provider, ['person']))
			})

			this.debouncedFind(this.searchQuery)
			unifiedSearchLogger.debug('Person filter applied', { person })
		},

		async loadMoreResultsForProvider(provider) {
			this.providerResultLimit += 5
			this.find(this.searchQuery, [provider])
		},

		addProviderFilter(providerFilter, loadMoreResultsForProvider = false) {
			unifiedSearchLogger.debug('Applying provider filter', { providerFilter, loadMoreResultsForProvider })
			if (!providerFilter.id) {
				return
			}
			if (providerFilter.isPluginFilter) {
				// There is no way to know what should go into the callback currently
				// Here we are passing isProviderFilterApplied (boolean) which is a flag sent to the plugin
				// This is sent to the plugin so that depending on whether the filter is applied or not, the plugin can decide what to do
				// TODO : In nextcloud/search, this should be a proper interface that the plugin can implement
				const isProviderFilterApplied = this.filteredProviders.some((provider) => provider.id === providerFilter.id)
				providerFilter.callback(!isProviderFilterApplied)
			}
			this.providerResultLimit = loadMoreResultsForProvider ? this.providerResultLimit : 5
			this.providerActionMenuIsOpen = false
			// With the possibility for other apps to add new filters
			// Resulting in a possible id/provider collision
			// If a user tries to apply a filter that seems to already exist, we remove the current one and add the new one.
			const existingFilterIndex = this.filteredProviders.findIndex((existing) => existing.id === providerFilter.id)
			if (existingFilterIndex > -1) {
				this.filteredProviders.splice(existingFilterIndex, 1)
				this.filters = this.syncProviderFilters(this.filters, this.filteredProviders)
			}
			this.filteredProviders.push({
				...providerFilter,
				type: providerFilter.type || 'provider',
				isPluginFilter: providerFilter.isPluginFilter || false,
			})
			this.filters = this.syncProviderFilters(this.filters, this.filteredProviders)
			unifiedSearchLogger.debug('Search filters (newly added)', { filters: this.filters })
			this.debouncedFind(this.searchQuery)
		},

		removeFilter(filter) {
			if (filter.type === 'provider') {
				for (let i = 0; i < this.filteredProviders.length; i++) {
					if (this.filteredProviders[i].id === filter.id) {
						this.filteredProviders.splice(i, 1)
						break
					}
				}
				this.filters = this.syncProviderFilters(this.filters, this.filteredProviders)
				unifiedSearchLogger.debug('Search filters (recently removed)', { filters: this.filters })
			} else {
				// Remove non provider filters such as date and person filters
				for (let i = 0; i < this.filters.length; i++) {
					if (this.filters[i].id === filter.id) {
						this.filters.splice(i, 1)
						this.enableAllProviders()
						break
					}
				}
			}
			this.debouncedFind(this.searchQuery)
		},

		syncProviderFilters(firstArray, secondArray) {
			// Create a copy of the first array to avoid modifying it directly.
			const synchronizedArray = firstArray.slice()
			// Remove items from the synchronizedArray that are not in the secondArray.
			synchronizedArray.forEach((item, index) => {
				const itemId = item.id
				if (item.type === 'provider') {
					if (!secondArray.some((secondItem) => secondItem.id === itemId)) {
						synchronizedArray.splice(index, 1)
					}
				}
			})
			// Add items to the synchronizedArray that are in the secondArray but not in the firstArray.
			secondArray.forEach((secondItem) => {
				const itemId = secondItem.id
				if (secondItem.type === 'provider') {
					if (!synchronizedArray.some((item) => item.id === itemId)) {
						synchronizedArray.push(secondItem)
					}
				}
			})

			return synchronizedArray
		},

		updateDateFilter() {
			const currFilterIndex = this.filters.findIndex((filter) => filter.id === 'date')
			if (currFilterIndex !== -1) {
				this.filters[currFilterIndex] = this.dateFilter
			} else {
				this.filters.push(this.dateFilter)
			}

			this.providers.forEach(async (provider, index) => {
				this.providers[index].disabled = !(await this.providerIsCompatibleWithFilters(provider, ['since', 'until']))
			})
			this.debouncedFind(this.searchQuery)
		},

		applyQuickDateRange(range) {
			this.dateActionMenuIsOpen = false
			const today = new Date()
			let startDate
			let endDate

			switch (range) {
				case 'today':
				// For 'Today', both start and end are set to today
					startDate = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0, 0)
					endDate = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59, 999)
					this.dateFilter.text = t('core', 'Today')
					break
				case '7days':
				// For 'Last 7 days', start date is 7 days ago, end is today
					startDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 6, 0, 0, 0, 0)
					this.dateFilter.text = t('core', 'Last 7 days')
					break
				case '30days':
				// For 'Last 30 days', start date is 30 days ago, end is today
					startDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 29, 0, 0, 0, 0)
					this.dateFilter.text = t('core', 'Last 30 days')
					break
				case 'thisyear':
				// For 'This year', start date is the first day of the year, end is the last day of the year
					startDate = new Date(today.getFullYear(), 0, 1, 0, 0, 0, 0)
					endDate = new Date(today.getFullYear(), 11, 31, 23, 59, 59, 999)
					this.dateFilter.text = t('core', 'This year')
					break
				case 'lastyear':
				// For 'Last year', start date is the first day of the previous year, end is the last day of the previous year
					startDate = new Date(today.getFullYear() - 1, 0, 1, 0, 0, 0, 0)
					endDate = new Date(today.getFullYear() - 1, 11, 31, 23, 59, 59, 999)
					this.dateFilter.text = t('core', 'Last year')
					break
				case 'custom':
					this.showDateRangeModal = true
					return
				default:
					return
			}
			this.dateFilter.startFrom = startDate
			this.dateFilter.endAt = endDate
			this.updateDateFilter()
		},

		setCustomDateRange(event) {
			unifiedSearchLogger.debug('Custom date range', { range: event })
			this.dateFilter.startFrom = event.startFrom
			this.dateFilter.endAt = event.endAt
			this.dateFilter.text = t(
				'core',
				'Between {startDate} and {endDate}',
				{
					startDate: this.dateFilter.startFrom!.toLocaleDateString([getCanonicalLocale()]),
					endDate: this.dateFilter.endAt!.toLocaleDateString([getCanonicalLocale()]),
				},
			)
			this.updateDateFilter()
		},

		handlePluginFilter(addFilterEvent) {
			unifiedSearchLogger.debug('Handling plugin filter', { addFilterEvent })
			for (let i = 0; i < this.filteredProviders.length; i++) {
				const provider = this.filteredProviders[i]
				if (provider.id === addFilterEvent.id) {
					provider.name = addFilterEvent.filterUpdateText
					// Filters attached may only make sense with certain providers,
					// So, find the provider attached, add apply the extra parameters to those providers only
					const compatibleProviderIndex = this.providers.findIndex((provider) => provider.id === addFilterEvent.id)
					if (compatibleProviderIndex > -1) {
						provider.extraParams = addFilterEvent.filterParams
						this.filteredProviders[i] = provider
					}
					break
				}
			}
			this.debouncedFind(this.searchQuery)
		},

		groupProvidersByApp(filters) {
			const groupedByProviderApp = {}

			filters.forEach((filter) => {
				const provider = filter.appId ? filter.appId : 'general'
				if (!groupedByProviderApp[provider]) {
					groupedByProviderApp[provider] = []
				}
				groupedByProviderApp[provider].push(filter)
			})

			const flattenedArray = []
			Object.values(groupedByProviderApp).forEach((group) => {
				flattenedArray.push(...group)
			})

			return flattenedArray
		},

		async providerIsCompatibleWithFilters(provider, filterIds) {
			return filterIds.every((filterId) => provider.filters?.[filterId] !== undefined)
		},

		async enableAllProviders() {
			this.providers.forEach(async (_, index) => {
				this.providers[index].disabled = false
			})
		},
	},
})
</script>

<style lang="scss" scoped>
.unified-search-modal-root :deep(.modal-container) {
	box-sizing: border-box;
	height: min(80vh, 800px);
}

:deep(.unified-search-modal .unified-search-modal__content) {
	display: flex;
	flex-direction: column;
	// No padding to prevent scrollbar misplacement
	padding-inline: 0;
}

.unified-search-modal {
	&__header {
		// Add background to prevent leaking scrolled content (because of sticky position)
		background-color: var(--color-main-background);
		// Fix padding to have the input centered
		padding-inline-end: 12px;
		// Some padding to make elements scrolled under sticky position look nicer
		padding-block-end: 12px;
		// Make it sticky with the input margin for the label
		position: sticky;
		top: 6px;
	}

	&__filters {
		display: flex;
		flex-wrap: wrap;
		gap: 4px;
		justify-content: start;
		padding-top: 4px;
	}

	&__search-external-resources {
		:deep(span.checkbox-content) {
			padding-top: 0;
			padding-bottom: 0;
		}

		:deep(.checkbox-content__icon) {
			margin: auto !important;
		}

		&--aligned {
			margin-inline-start: auto;
		}
	}

	&__filters-applied {
		padding-top: 4px;
		display: flex;
		flex-wrap: wrap;
	}

	&__no-content {
		display: flex;
		align-items: center;
		margin-top: 0.5em;
		height: 70%;
	}

	&__results {
		overflow: hidden scroll;
		// Adjust padding to match container but keep the scrollbar on the very end
		padding-inline: 0 12px;
		padding-block: 0 12px;

		.result {
			&-title {
				color: var(--color-primary-element);
				font-size: 16px;
				margin-block: 8px 4px;
			}

			&-footer {
				justify-content: space-between;
				align-items: center;
				display: flex;
			}
		}

	}
}

.filter-button__icon {
	height: 20px;
	width: 20px;
	object-fit: contain;
	filter: var(--background-invert-if-bright);
	padding: 11px; // align with text to fit at least 44px
}

// Ensure modal is accessible on small devices
@media only screen and (max-height: 400px) {
	.unified-search-modal__results {
		overflow: unset;
	}
}
</style>
